home *** CD-ROM | disk | FTP | other *** search
- /* -*-c-*- */
-
- /* Copyright (C) 1989, 1992 Free Software Foundation, Inc.
-
- This file is part of GNU CC.
-
- GNU CC is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- GNU CC is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNU CC; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* As a special exception, if you link this library with files
- compiled with GCC to produce an executable, this does not cause
- the resulting executable to be covered by the GNU General Public License.
- This exception does not however invalidate any other reasons why
- the executable file might be covered by the GNU General Public License. */
-
- /*
- $Header: /usr/user/dennis_glatting/ObjC/c-runtime/dispatch/RCS/core.c,v 1.2 1992/08/18 04:46:58 dglattin Exp $
- $Author: dglattin $
- $Date: 1992/08/18 04:46:58 $
- $Log: core.c,v $
- * Revision 1.2 1992/08/18 04:46:58 dglattin
- * Saving a working version before release.
- *
- * Revision 1.1 1992/04/13 11:43:08 dennisg
- * Initial revision
- *
- */
-
- #include <assert.h>
- #include <ctype.h>
- #include <memory.h>
-
- #include <hash.h>
- #include <objc.h>
- #include <objcP.h>
- #include <objc-proto.h>
- #include <objc-protoP.h>
-
-
- #define MODULE_HASH_SIZE 32 /* Initial module hash table
- size. Value really doesn't
- matter. */
-
-
- #define CLASS_HASH_SIZE 32 /* Initial number of buckets
- size of class hash table. */
-
-
- /* Forward declare some functions. */
- id objc_object_create (Class_t),
- objc_object_dispose (id),
- objc_object_realloc (id, u_int),
- objc_object_copy (id);
- void objc_error (id aObject, const char* fmt, va_list ap);
- static id nilMethodIMP (id, SEL, ...);
- static id returnErrorStaticIMP (id, SEL, ...);
- static IMP handleRuntimeError (id, SEL);
- static void initializeDispatchTables (void);
- static SEL recordSelector (const char*);
- static void recordMethodsFromClass (Class_t);
- static void recordMethodsFromMethodList (MethodList_t);
- static void initializeClass (const char*);
-
-
- /*
- * This is a hash table of Class_t structures.
- *
- * At initialization the executable is searched for all Class_t structures.
- * Since these structures are created by the compiler they are therefore
- * located in the TEXT segment.
- *
- * A identical structure is allocated from the free store and initialized from
- * its TEXT counterpart and placed in the hash table using the TEXT part as
- * its key.
- *
- * Since the free store structure is now writable, additional initialization
- * takes place such as its "info" variable, method cache allocation, and
- * linking of all of its method and ivar lists from multiple implementations.
- */
- Cache_t classHash = NULL;
-
- /*
- * This variable is a flag used within the messaging routines. If a
- * application sets it to !0 then the messager will print messages sent to
- * objects.
- */
- BOOL objc_trace = NO;
-
- /* This mutex provides a course lock for method dispatch. */
- MUTEX runtimeMutex;
-
- /*
- * This hash table is used by the initialization routines. When the
- * constructor function (__objc_execClass) is called it is passed a pointer
- * to a module structure. That pointer is stored in this table and its
- * contents are processed in __objcInit().
- */
- Cache_t moduleHash = NULL;
-
- /*
- * This flag is used by the messager routines to determine if the run-time
- * has been initialized. If the run-time isn't initialized then a
- * initialization clean up routine is called.
- */
- static int runtimeInitialized = 0;
-
- /*
- * Records that hold pointers to arrays of records. The terminal records are
- * method implementations.
- *
- * The size of the first record is the number of unique classes in the
- * executable. The second is the number of selectors.
- *
- * The second record conatins methods that are visible to the class -- that is,
- * methods that are not overriden from the classt to the root object.
- *
- * The cache pointers of class and meta class structures point to one of these
- * records.
- */
- static Record_t instanceMethodRecord = NULL;
- static Record_t factoryMethodRecord = NULL;
-
- /*
- * This structure is used to translate between selectors and their ASCII
- * representation. A NULL terminated array of char*,
- * OBJC_SELECTOR_REFERENCES, is passed to the constructor routine:
- * __objc_execClass(). That routine places entries from that array into this
- * structure. The location within OBJC_SELECTOR_REFERENCES where the string
- * was taken from is replaced with a small integer, the index into the array
- * inside selectorTranslateTable. That integer then becomes the selector.
- *
- * Selectors begin at 1 to numEntries. A selector outside of that range is
- * considered an error. The selector integers are used as the first index
- * into the instanceMethodRecord and factoryMethodRecord arrays.
- */
- static Record_t selectorRecord = NULL;
-
- /*
- * This data structure is used in the special case where usual fatal error
- * functions are called but have been overridden in a class. The value
- * returned by that function is returned to the calling object. errorStatic
- * holds the returned value until it is retrieved by returnErrorStaticIMP
- * which returns it to the calling function.
- */
- static id errorStatic;
-
-
- /* Given a class and selector, return the selector's implementation. */
- static inline IMP
- getIMP (Class_t aClass, SEL aSel) {
-
- IMP theIMP = NULL;
-
-
- theIMP = record_get (getClassNumber (aClass),
- record_get ((u_int)aSel, *aClass->cache));
-
- return theIMP;
- }
-
-
- static inline char*
- strdup (const char* str) {
-
- char* newStr = malloc (strlen (str) + 8);
-
-
- assert (newStr);
- sprintf (newStr, "%s", str);
-
- return newStr;
- }
-
-
- /*
- * This function is called by constructor functions generated for each module
- * compiled.
- *
- * The purpose of this function is to gather the module pointers so that they
- * may be processed by the initialization clean up routine.
- */
- void
- __objc_execClass (Module_t aModule) {
-
-
- assert(aModule->size == sizeof (Module));
- DEBUG_PRINTF (stderr, "received load module: %s\n",aModule->name);
-
- /* Allocate the module hash table if it doesn't exist. */
- if (!moduleHash)
- moduleHash = hash_new (MODULE_HASH_SIZE,
- (HashFunc)intHash, (CompareFunc)intCmp);
-
- /* Save the module pointer for later processing. */
- hash_add (&moduleHash, aModule, aModule);
- }
-
-
- /*
- * This function is called by the executable before main (). Its purpose is
- * to initialize the ObjC run-time system.
- */
- void
- objcInitCleanup (void) {
-
- MetaClass_t classObject;
- CacheNode_t aNode;
- u_int i;
-
-
- /* Header file data structure hack test. */
- assert(sizeof (Class) == sizeof (MetaClass));
-
- /* Allocate and initialize the mutex. */
- MUTEX_ALLOC( &runtimeMutex );
- MUTEX_INIT( runtimeMutex );
-
- /* Enable malloc debugging. This'll slow'er down! */
- #ifdef DEBUG
- malloc_debug (62);
- #endif
-
- /* Allocate the selector translation record. */
- selectorRecord = record_new ();
-
- /* For all of the modules collected in the constructor function.
- Make a first pass through the modules and process some of the data. */
- for (aNode = hash_next (moduleHash, NULL); aNode;
- aNode = hash_next (moduleHash, aNode)) {
- Module_t aModule = aNode->theValue;
- Symtab_t theSymtab = aModule->symtab;
- SEL *(*selectors)[] = (SEL* (*)[])theSymtab->refs;
-
- DEBUG_PRINTF (stderr, "parsing load module: %s\n",aModule->name);
- for (i = 0; i < theSymtab->cls_def_cnt; ++i) {
- Class_t theClass = (Class_t)theSymtab->defs[ i ];
-
- /* Make sure we have what we think. */
- assert(theClass->info & CLS_CLASS);
- assert(theClass->isa->info & CLS_META);
-
- DEBUG_PRINTF (stderr, "phase 1, processing class: %s\n", theClass->name);
-
- /* Store the class in the class table and assign class numbers. */
- addClassToHash(theClass);
-
- /* Store all of the selectors in the class and meta class. */
- recordMethodsFromClass (theClass);
- recordMethodsFromClass ((Class_t)theClass->isa);
-
- /* Initialize the cache pointers. */
- theClass->cache = &instanceMethodRecord;
- theClass->isa->cache = &factoryMethodRecord;
- }
-
- /* Replace referenced selectors. */
- if (selectors)
- for(i = 0; (*selectors)[ i ]; ++i)
- (*selectors)[ i ] = recordSelector ((const char*)(*selectors)[ i ]);
- }
-
- /* Get the meta class object for connection processing. */
- classObject = objc_getMetaClass( "Object" );
- assert( classObject );
- assert( classObject->info & CLS_META );
-
- /* Connect the classes together. */
- for (aNode = hash_next (classHash, NULL); aNode;
- aNode = hash_next (classHash, aNode)) {
-
- Class_t theClass = aNode->theValue;
-
- /* Make sure we have what we think. */
- assert(theClass->info & CLS_CLASS);
- assert(theClass->isa->info & CLS_META);
-
- DEBUG_PRINTF (stderr, "phase 2, processing class: %s\n", theClass->name);
-
- /* The isa pointer of all meta classes point to Object's meta class. */
- theClass->isa->isa = classObject;
-
- /* Assign super class pointers */
- if (theClass->super_class)
- theClass->super_class = objc_getClass ((char*)theClass->super_class);
- if (theClass->isa->super_class)
- theClass->isa->super_class = theClass->super_class->isa;
- }
-
- /* Interconnect the category information. */
- for (aNode = hash_next (moduleHash, NULL); aNode;
- aNode = hash_next (moduleHash, aNode)) {
-
- Module_t theModule = aNode->theValue;
- Symtab_t theSymtab = theModule->symtab;
- int j;
-
- DEBUG_PRINTF (stderr, "processing categories from: %s\n", theModule->name);
-
- /*
- * Connect the category information.
- *
- * Place instance and class category methods at the head of the
- * class's method list.
- */
- for (j = 0; j < theSymtab->cat_def_cnt; ++j) {
- Category_t theCategory = theSymtab->defs[ j + theSymtab->cls_def_cnt ];
-
- /* Do instance methods. */
- if (theCategory->instance_methods)
- addMethodsToClass (objc_getClass (theCategory->class_name),
- theCategory->instance_methods);
-
- /* Do class methods. */
- if (theCategory->class_methods)
- addMethodsToClass
- ((Class_t)objc_getMetaClass (theCategory->class_name),
- theCategory->class_methods);
- }
- }
-
- initializeDispatchTables();
-
- /* Prevent future calls to the clean up routine. */
- runtimeInitialized = 1;
-
- /* Print out class tables if debugging. */
- DEBUG_PRINTF (stderr, "dump of class tables from objcInit()\n");
- debug_dump_classes();
-
- }
-
-
- IMP
- objc_msgSend (id theReceiver, SEL aSel) {
-
- /*
- * A method is always called by the compiler. If a method wasn't
- * found then supply a default.
- */
- IMP theIMP = nilMethodIMP;
-
-
- /* The run time must be initialized at this point.
- Otherwise we get a message sent to a object with a bogus selector. */
- assert(runtimeInitialized);
-
- /* Objective-C allows messages to be sent to a nil object. */
- if (theReceiver) {
-
- /* Check for common programmer error. */
- if (!theReceiver->isa) {
- fprintf (stderr, "method %s sent to deallocated object %#x\n",
- sel_getName (aSel), theReceiver);
- abort ();
- }
-
- /* Initialize the class if need be. */
- if( !( theReceiver->isa->info & CLS_INITIALIZED ))
- initializeClass (theReceiver->isa->name);
-
- /*
- * If we're passed a object then its isa is a Class. If
- * we're passed a Class then its isa is a MetaClass.
- * Therefore, searching for a instance or class method
- * requires no special decision making here.
- *
- * Look for the method.
- */
- theIMP = getIMP (theReceiver->isa, aSel);
-
- /* If the method cannot be found then perform error handling. */
- if (!theIMP)
- theIMP = handleRuntimeError (theReceiver, aSel);
- }
-
- /* Nice debugging messages if enabled. */
- if (objc_trace) {
- printf ("trace: objc_msgSend (), obj=%#x, class=%s, method=%s\n",
- theReceiver,
- theReceiver->isa->name,
- sel_getName (aSel));
- fflush (stdout);
- }
-
- return theIMP;
- }
-
-
- IMP
- objc_msgSendSuper (Super_t superContext, SEL aSel) {
-
- IMP theIMP;
-
-
- assert(runtimeInitialized);
-
- if( !( superContext->class->info & CLS_INITIALIZED ))
- initializeClass (superContext->class->name);
- if( !( superContext->receiver->isa->info & CLS_INITIALIZED ))
- initializeClass (superContext->receiver->isa->name);
-
- theIMP = getIMP (superContext->class, aSel);
-
- if (!theIMP)
- theIMP = handleRuntimeError (superContext->receiver, aSel);
-
- if (objc_trace) {
- printf ("trace: objc_msgSendSuper (), obj=%#x, class=%s, method=%s\n",
- superContext->receiver,
- superContext->receiver->isa->name,
- sel_getName (aSel));
- fflush (stdout);
- }
-
- return theIMP;
- }
-
-
- /*
- * This function is called by objc_msgSend() or objc_msgSendSuper() when a
- * message is sent to a object which it does not recognize.
- */
- static IMP
- handleRuntimeError (id theObject, SEL aSel) {
-
- IMP theIMP;
-
-
- /*
- * If the object recognizes the doesNotRecognize: method then we're
- * going to send it.
- */
- theIMP = getIMP (theObject->isa, sel_getUid ("doesNotRecognize:"));
- if (theIMP)
- errorStatic = (*theIMP)(theObject, sel_getUid ("doesNotRecognize:"), aSel);
- else {
- /*
- * The object doesn't recognize the method. Check for
- * responding to error:. If it does then sent it.
- */
- char msg[ 256
- + strlen (sel_getName (aSel))
- + strlen (theObject->isa->name) ];
-
- sprintf (msg, "%s does not recognize %s",
- theObject->isa->name, sel_getName (aSel));
-
- theIMP = getIMP (theObject->isa, sel_getUid ("error:"));
- if (theIMP)
- errorStatic = (*theIMP)(theObject, sel_getUid ("error:"), msg);
- else {
- /*
- * The object doesn't respond to doesNotRecognize: or
- * error:; Therefore, a default action is taken.
- */
- fprintf (stderr, "%s\n", msg);
- abort ();
- }
- }
-
- /*
- * Either doesNotRecognize: or error: has been overridden. We have
- * to return that value as the default action.
- */
- return returnErrorStaticIMP;
- }
-
-
- /*
- * This function is used by the run-time to provide a method where nil
- * objects can receive messages.
- *
- * This method simply returns self.
- */
- static id
- nilMethodIMP (id aObject, SEL aSel, ...) {
-
-
- return aObject;
- }
-
-
- /*
- * This function is used by the run-time to provide a method where nil
- * objects can receive messages.
- *
- * This method simply returns self.
- *
- * Note: multiple thread problem area.
- */
- static id
- returnErrorStaticIMP (id aObject, SEL aSel, ...) {
-
-
- return errorStatic;
- }
-
-
- /*
- * These variables provide a way for the defalut methods of object
- * allocation, destruction, and reallocation to be overridden.
- */
- id (*_alloc)(Class_t) = objc_object_create;
- id (*_dealloc)(id) = objc_object_dispose;
- id (*_realloc)(id, u_int) = objc_object_realloc;
- id (*_copy)(id) = objc_object_copy;
- void (*_error)(id, const char*, va_list) = objc_error;
-
-
- id
- objc_object_create (Class_t aClass) {
-
- id aObject;
-
-
- assert( aClass );
-
- /*
- * Allocate memory for the object, initialize the memory to 0, and
- * set the object's isa pointer.
- *
- * The object's isa pointer is the class's TEXT image. It is used by
- * the messager as the key to the class hash for methods.
- *
- * No need to initialize the class. That was done in objcInit().
- */
- aObject = calloc (1, aClass->instance_size);
- assert(aObject);
- aObject->isa = aClass;
-
- return aObject;
- }
-
-
- id
- objc_object_dispose (id aObject) {
-
-
- aObject->isa = NULL;
- free (aObject);
-
- return nil;
- }
-
-
- id
- objc_object_realloc (id aObject, u_int numBytes) {
-
- id reallocObj;
-
-
- /* Can't resize a object smaller than its instance size. */
- assert (numBytes >= aObject->isa->instance_size);
-
- reallocObj = realloc (aObject, numBytes);
- memset (((char*)reallocObj) + aObject->isa->instance_size,
- 0, (numBytes - aObject->isa->instance_size));
-
- return reallocObj;
- }
-
-
- id
- objc_object_copy (id aObject) {
-
- id newObj;
-
-
- newObj = class_createInstance (aObject->isa);
- memcpy (newObj, aObject, objc_classSize (aObject));
-
- return newObj;
- }
-
-
- void
- objc_error (id aObject, const char* fmt, va_list ap) {
-
-
- vfprintf (stderr, fmt, ap);
- abort ();
- }
-
-
- /* Silly function to skip past a sequence of digits in a string. */
- static inline const char*
- skipDigits (const char* str) {
-
- while (isdigit (*str))
- ++str;
-
- return str;
- }
-
-
- u_int
- method_getNumberOfArguments (Method_t aMethod) {
-
- u_int num = 0;
- const char* args = &aMethod->method_types[1];
-
-
- while (*args) {
-
- /* Skip past size info. */
- args = skipDigits (args);
-
- /* Argument type next. */
- assert(*args);
- ++num;
-
- /* Step to next arg. */
- ++args;
- }
-
- assert(num >= 2);
- return num;
- }
-
-
- u_int
- method_getArgumentInfo (Method_t aMethod, int indx, const char **type,
- int *offset) {
-
- const char* args = skipDigits (&aMethod->method_types[1]);
- int i;
-
-
- assert(method_getNumberOfArguments (aMethod) >= indx);
-
- /* Step to arg. */
- for (i = 0; i < indx; ++i) {
- ++args;
- args = skipDigits (args);
- }
-
- /* Return arg data. */
- *type = args++;
- *offset = atoi (args);
-
- return indx;
- }
-
-
- /* This function is not thread safe. */
- Ivar_t
- object_getIvarAddress (id aObject, const char* variableName) {
-
- Class_t scanClass = aObject->isa; /* Here is the thread safe problem. */
- Ivar_t theIvarSought = NULL;
-
-
- do {
- IvarList_t ivarList = scanClass->ivars;
- int i;
-
- /* Look at all of the ivar names. */
- for (i = 0; i < ivarList->ivar_count; ++i)
- if (!strcmp (variableName, ivarList->ivar_list[ i ].ivar_name))
- theIvarSought = &ivarList->ivar_list[ i ];
-
- /*
- * If the ivar wasn't found then lets look to the
- * super class.
- *
- * If the class is Object then the super class is NULL
- * and we're done.
- */
- scanClass = scanClass->super_class;
-
- } while (!theIvarSought && scanClass);
-
- return theIvarSought;
- }
-
-
- /*
- * Search for a method starting from the current class up its hierarchy.
- *
- * Return a pointer to the method's method structure if found. NULL otherwise.
- */
- Method_t
- searchForMethodInHierarchy (Class_t aClass, SEL aSel) {
-
- Method_t theMethodSought = NULL;
- const char* selName = sel_getName (aSel);
-
-
- /*
- * Scan the method list of the class. If the method isn't found in
- * the list then step to its super class.
- */
- do {
-
- theMethodSought = searchForMethodInList (aClass->methods, selName);
- aClass = aClass->super_class;
-
- } while (!theMethodSought && aClass);
-
- return theMethodSought;
- }
-
-
- /*
- * Given a linked list of method and a method's name. Search for the named
- * method's method structure.
- *
- * Return a pointer to the method's method structure if found. NULL otherwise.
- */
- Method_t
- searchForMethodInList (MethodList_t aList, const char* selName) {
-
- MethodList_t aMethodList = aList;
-
-
- /* Check for bumbling. */
- assert(selName);
-
- /* If not found then we'll search the list. */
- while (aMethodList) {
- int i;
-
- /* Search the method list. */
- for (i = 0; i < aMethodList->method_count; ++i) {
- Method_t aMethod = &aMethodList->method_list[ i ];
-
- if (aMethod->method_name)
- if (!strcmp (aMethod->method_name, selName))
- return aMethod;
- }
-
- /* The method wasn't found. Follow the link to the next list of
- methods. */
- aMethodList = aMethodList->method_next;
- }
-
- return NULL;
- }
-
-
- /*
- * This function adds a method list to a class.
- *
- * This function is typically called by another function specific to the
- * run-time. As such this function does not worry about thread safe issued.
- */
- void
- addMethodsToClass (Class_t aClass, MethodList_t newList) {
-
- int i;
-
-
- /* Passing of a linked list is not allowed. Do multiple calls. */
- assert(!newList->method_next);
-
- /* Check for duplicates. */
- for (i = 0; i < newList->method_count; ++i) {
- Method_t aMethod = &newList->method_list[i];
-
- if (aMethod->method_name) /* Sometimes these are NULL */
- if (searchForMethodInList (aClass->methods, aMethod->method_name)) {
- /*
- * Duplication. Print a error message an change the
- * method name to NULL.
- */
- fprintf (stderr, "attempt to add a existing method: %s\n",
- aMethod->method_name);
- aMethod->method_name = NULL;
- }
- }
-
- /* Add the methods to the class's method list. */
- newList->method_next = aClass->methods;
- aClass->methods = newList;
- }
-
-
- /*
- * This function removes the instance and factory methods in the passed list
- * from a class.
- *
- * Methods are removed from a class by replacing the method's name with NULL.
- *
- *
- * This function is typically called by another function specific to the
- * run-time. As such this function does not worry about thread safe issued.
- */
- void
- class_removeMethods (Class_t aClass, MethodList_t aMethodList) {
-
- int i;
-
-
- /* Passing of a linked list is not allowed. Do multiple calls. */
- assert(!aMethodList->method_next);
-
- /*
- * For each method in the list search the method lists erasing any
- * entries found.
- */
- for (i = 0; i < aMethodList->method_count; ++i) {
- Method_t killMethod = &aMethodList->method_list[i];
- Method_t aMethod;
-
- /* Remove any instance method found. */
- aMethod = searchForMethodInList (aClass->methods,
- killMethod->method_name);
- if (aMethod)
- aMethod->method_name = NULL;
-
- /* Remove any factory method found. */
- aMethod = searchForMethodInList (aClass->isa->methods,
- killMethod->method_name);
- if (aMethod)
- aMethod->method_name = NULL;
- }
- }
-
-
- /*
- * This is a incomplete implementation of posing. This function does the
- * bulk of the work but does not initialize the class method caches. That is
- * a run-time specific operation.
- *
- * I implement posing by hiding theSuperClass, creating new class and meta
- * class structures, initializing it with theImposter, and changing it such
- * that it is identified as theSuperClass. theSuperClass remains in the
- * hierarchy but is inaccessible by the means. The class hierarchy is then re
- * arranged such that all of the subclasses of theSuperClass now inherit from
- * the new class structures -- except the impostor itself. The only dramatic
- * effect on the application is that subclasses of theSuperClass cannot do a
- * [ .... superClass ] and expect their real super class.
- */
- Class_t
- class_poseAs (Class_t theImpostor, Class_t theSuperClass) {
-
- Class_t newClass = calloc (1, sizeof (Class));
- MetaClass_t newMetaClass = calloc (1, sizeof (MetaClass));
- CacheNode_t aNode;
- char* newName = malloc (strlen (theSuperClass->name) + 12);
-
-
- assert(newClass);
- assert(newMetaClass);
- assert(newName);
-
- /* No dispatching while the the posing class is being built.
- The dispatch tables will be hacked on. */
- MUTEX_LOCK( runtimeMutex );
-
- assert(theImpostor->info & CLS_CLASS);
- assert(theSuperClass->info & CLS_CLASS);
-
- assert(theImpostor->instance_size == theSuperClass->instance_size);
-
- /* Create the impostor class. */
- newClass->isa = newMetaClass;
- newClass->super_class = theSuperClass;
- newClass->name = theSuperClass->name;
- newClass->version = theSuperClass->version;
- newClass->info = theSuperClass->info;
- newClass->instance_size = theSuperClass->instance_size;
- newClass->ivars = theSuperClass->ivars;
- newClass->methods = theImpostor->methods;
- newClass->cache = &instanceMethodRecord;
-
- /* Create the impostor meta class. */
- newMetaClass->isa = theSuperClass->isa->isa;
- newMetaClass->super_class = theSuperClass->isa->super_class;
- newMetaClass->name = theSuperClass->isa->name;
- newMetaClass->version = theSuperClass->isa->version;
- newMetaClass->info = theSuperClass->isa->info;
- newMetaClass->instance_size = theSuperClass->isa->instance_size;
- newMetaClass->ivars = theSuperClass->isa->ivars;
- newMetaClass->methods = theImpostor->isa->methods;
- newMetaClass->cache = &factoryMethodRecord;
-
- /*
- * Delete the class from the hash table, change its name so that it
- * can no longer be found, then place it back into the hash table
- * using its new name.
- *
- * Don't worry about the class number. It is already assigned.
- *
- * Don't worry about dangling pointers. Life's a bitch. (A little bit
- * of memory is lost with the hash key.)
- */
- hash_remove (classHash, theSuperClass->name);
- sprintf (newName, "%s*", theSuperClass->name);
- theSuperClass->name = newName;
- theSuperClass->isa->name = newName;
- hash_add (&classHash, theSuperClass->name, theSuperClass);
-
- /*
- * Now change all of the classes derived from theSuperClass to be
- * derived from a impostor (except the impostor's impostor.
- */
- for (aNode = hash_next (classHash, NULL); aNode;
- aNode = hash_next (classHash, aNode)) {
-
- Class_t theClass = aNode->theValue;
-
- if (theClass->super_class == theSuperClass)
- if (theClass != theImpostor)
- theClass->super_class = newClass;
- }
-
- /* Place the impostor class in class hash table
- and assign it a class number. */
- addClassToHash (newClass);
-
- /* Reinitialize the dispatch tables. */
- initializeDispatchTables ();
-
- MUTEX_UNLOCK( runtimeMutex );
-
- /* Print out class tables if debugging. */
- DEBUG_PRINTF (stderr, "dump of class tables class_poseAs()\n");
- debug_dump_classes();
-
- return newClass;
- }
-
-
- /*
- * This routine is given a class and records all of the methods in its class
- * structure in the record table.
- */
- static void
- recordMethodsFromClass (Class_t aClass) {
-
- MethodList_t methodList;
-
-
- methodList = aClass->methods;
- while(methodList) {
- recordMethodsFromMethodList(methodList);
- methodList = methodList->method_next;
- }
- }
-
-
- /*
- * This routine is given a list of methods and records each of the methods in
- * the record table. This is the routine that does the actual recording
- * work.
- */
- static void
- recordMethodsFromMethodList (MethodList_t aMethodList) {
-
- int i;
-
-
- for(i = 0; i < aMethodList->method_count; ++i) {
- Method_t aMethod = &aMethodList->method_list[ i ];
-
- recordSelector(aMethod->method_name);
- }
- }
-
-
- SEL
- sel_getUid (const STR aName) {
-
- int i;
-
-
- for (i = 1; i <= record_entries (selectorRecord); ++i)
- if (!strcmp (aName, record_get (i, selectorRecord)))
- return (SEL)i;
-
- /* Unable to locate selector. Return error value. */
- return (SEL)0;
- }
-
-
- const STR
- sel_getName (SEL aSelector) {
-
-
- return record_get ((u_int)aSelector, selectorRecord);
- }
-
-
- /*
- * Store the passed selector name in the selector record and return its
- * selector value (value returned by sel_getUid()).
- */
- static SEL
- recordSelector (const char* aSel) {
-
- int j;
-
-
- /* Find either the selector in the table or an empty slot. */
- for (j = 1; j <= record_entries (selectorRecord); ++j)
- if (!strcmp (aSel, record_get (j, selectorRecord)))
- return (SEL)j;
-
- /* Save the selector name. */
- record_store (strdup (aSel), selectorRecord);
- DEBUG_PRINTF (stderr, "Record: %s as: %#x\n", aSel, j);
-
- return (SEL)j;
- }
-
-
- /*
- * Initialize the dispatch tables. This requires the initialization of the
- * instanceMethodRecord and factoryMethodRecord arrays and the arrays they
- * point to.
- *
- * The first array is indexed by a class number. Therefore its size is the
- * number of classes in the executable. The second array is indexed by a
- * selector id. Therefore its size is the number of unique selectors in the
- * application.
- *
- * When a method is sent to a object its class number is extracted from the
- * class structure and used in the first array. The selector id is used in
- * the second. The result value is a method implementation.
- */
- static void
- initializeDispatchTables (void) {
-
- int i;
-
-
- /* Check to make sure things are in place. */
- assert(selectorRecord);
-
- /* Blow away the instance and factory method records. */
- if (factoryMethodRecord) {
- for (i = 1; i <= record_entries (factoryMethodRecord); ++i)
- record_delete (record_get(i, factoryMethodRecord));
- record_delete (factoryMethodRecord);
- }
- if (instanceMethodRecord) {
- for (i = 1; i <= record_entries (instanceMethodRecord); ++i)
- record_delete (record_get(i, instanceMethodRecord));
- record_delete (instanceMethodRecord);
- }
-
- /* Reallocate the instance and factory method records. */
- factoryMethodRecord = record_new ();
- instanceMethodRecord = record_new ();
- for (i = 1; i <= record_entries (selectorRecord); ++i) {
- record_store (record_new(), factoryMethodRecord);
- record_store (record_new(), instanceMethodRecord);
- }
-
- /* Fool all of the secondary records into thinking they have data. */
- for (i = 1; i <= record_entries (selectorRecord); ++i) {
- Record_t aRecord;
- CacheNode_t aNode;
-
- aRecord = record_get (i, factoryMethodRecord);
- for (aNode = hash_next (moduleHash, NULL); aNode;
- aNode = hash_next (moduleHash, aNode))
- record_store (NULL, aRecord);
-
- aRecord = record_get (i, instanceMethodRecord);
- for (aNode = hash_next (moduleHash, NULL); aNode;
- aNode = hash_next (moduleHash, aNode))
- record_store (NULL, aRecord);
- }
-
- /* For all classes fill in the methods implemented by the class and visiable
- from the class in the hierarchy. Those methods are assigned to the
- class. */
- for (i = 1; i <= record_entries (selectorRecord); ++i) { /* i is a sel */
- CacheNode_t aNode;
-
- for (aNode = hash_next (classHash, NULL); aNode;
- aNode = hash_next (classHash, aNode)) {
- Class_t aClass = aNode->theValue;
- MetaClass_t aMetaClass = aClass->isa;
- int classNum = getClassNumber (aClass);
- Method_t aMethod;
-
- /* DEBUG_PRINTF (stderr, "Assignment of sel=%s, class=%s (%#x, %#x)\n",
- sel_getName ((SEL)i), aClass->name,
- searchForMethodInHierarchy (aClass, (SEL)i),
- searchForMethodInHierarchy ((Class_t)aMetaClass, (SEL)i)); */
-
- aMethod = searchForMethodInHierarchy (aClass, (SEL)i);
- if (aMethod)
- record_store_at (classNum, aMethod->method_imp,
- record_get (i, instanceMethodRecord));
-
- assert( classNum == getClassNumber ((Class_t)aClass->isa));
- aMethod = searchForMethodInHierarchy ((Class_t)aMetaClass, (SEL)i);
- if (aMethod)
- record_store_at (classNum, aMethod->method_imp,
- record_get (i, factoryMethodRecord));
- }
- }
- }
-
-
- /*
- * This method is called by the dispatch routines when a class has not been
- * initialized. This method is responsible for initializing the class. This
- * is accomplished by first testing the class itself for responding to the
- * +initialize method. If such a method is implemented then it is called.
- * Before exit, irregardless if the class implements +initialize, the class
- * is marked as initialized.
- */
- static void
- initializeClass (const char* theClassName) {
-
-
- Method_t theMethodSought = NULL;
- Class_t theClass = objc_getClass (theClassName);
- SEL theSel = sel_getUid ("initialize");
-
-
- /* The class should not be initialized at this point. */
- assert( !(theClass->info & CLS_INITIALIZED ));
- assert( !(theClass->isa->info & CLS_INITIALIZED ));
-
- /* Search for the +initialize method.
- Call it if it exists. */
- theMethodSought =
- searchForMethodInList (theClass->isa->methods, sel_getName (theSel));
- if (theMethodSought) {
- IMP theIMP;
-
- DEBUG_PRINTF (stderr, "Class: %s sending +%s\n",
- theClassName, sel_getName (theSel));
- theIMP = getIMP ((Class_t)theClass->isa, theSel);
- assert(theIMP);
- (*theIMP)(theClass, theSel);
- }
-
- /* Mark the class as initialized. */
- theClass->info |= CLS_INITIALIZED;
- theClass->isa->info |= CLS_INITIALIZED;
- }
-
-
- /*
- * Silly little function that checks to make sure the class hash table is
- * initialized. If it isn't initialized then do it.
- */
- static inline void
- classHashInit( void ) {
-
- static u_int init = 0;
-
-
- if( !init )
- classHash = hash_new(CLASS_HASH_SIZE,
- (HashFunc)strHash, (CompareFunc)strCmp);
- init = 1;
- }
-
-
- Class_t
- objc_getClass( const char* aClassName) {
-
- Class_t aClass;
-
-
- /* Make sure the class hash table exists. */
- classHashInit();
-
- aClass = hash_value_for_key( classHash, aClassName );
-
- return aClass;
- }
-
-
- MetaClass_t
- objc_getMetaClass( const char* aClassName) {
-
-
- /* Meta classes are pointed to by the class's isa.
- Just get the class and return its isa. */
- return( objc_getClass( aClassName ))->isa;
- }
-
-
- void
- addClassToHash(Class_t aClass) {
-
- Class_t hClass;
-
-
- classHashInit();
-
- /* Check to see if the class is already in the hash table. */
- hClass = hash_value_for_key( classHash, aClass->name);
- if( !hClass ) {
- /* The class isn't in the hash
- table. Add the class and
- assign a class number. */
- static u_int classNum = 1;
-
- setClassNumber( aClass, classNum);
- setClassNumber((Class_t)aClass->isa, classNum);
- ++classNum;
-
- hash_add( &classHash, aClass->name, aClass);
- }
- }
-
-
- void
- debug_dump_classes( void) {
-
- CacheNode_t aNode;
- int i;
-
-
- DEBUG_PRINTF( stderr, "class tables\n");
- i = 0;
- for( aNode = hash_next( classHash, NULL); aNode;
- aNode = hash_next( classHash, aNode)) {
-
- Class_t theClass = aNode->theValue;
-
- DEBUG_PRINTF( stderr,
- "Class { /*%#x*/\n", theClass);
- DEBUG_PRINTF( stderr,
- " MetaClass_t isa = %#x\n", theClass->isa);
- DEBUG_PRINTF( stderr,
- " Class_t super_class = %#x\n", theClass->super_class);
- DEBUG_PRINTF( stderr,
- " char* name = %s\n", theClass->name);
- DEBUG_PRINTF( stderr,
- " long version = %ld\n", theClass->version);
- DEBUG_PRINTF( stderr,
- " long info = %#x\n", theClass->info);
- DEBUG_PRINTF( stderr,
- " long instance_size = %ld\n", theClass->instance_size);
- DEBUG_PRINTF( stderr,
- " IvarList_t ivars = %#x\n", theClass->ivars);
- DEBUG_PRINTF( stderr,
- " MethodList_t methods = %#x\n", theClass->methods);
- DEBUG_PRINTF( stderr,
- " Cache_t cache = %#x\n", theClass->cache);
- DEBUG_PRINTF( stderr, "}[%d];\n", i++);
- }
-
- i = 0;
- for( aNode = hash_next( classHash, NULL); aNode;
- aNode = hash_next( classHash, aNode)) {
-
- Class_t theClass = (Class_t)((Class_t)(aNode->theValue))->isa;
-
- DEBUG_PRINTF( stderr,
- "MetaClass { /*%#x*/\n", theClass);
- DEBUG_PRINTF( stderr,
- " MetaClass_t isa = %#x\n", theClass->isa);
- DEBUG_PRINTF( stderr,
- " MetaClass_t super_class = %#x\n", theClass->super_class);
- DEBUG_PRINTF( stderr,
- " char* name = %s\n", theClass->name);
- DEBUG_PRINTF( stderr,
- " long version = %ld\n", theClass->version);
- DEBUG_PRINTF( stderr,
- " long info = %#x\n", theClass->info);
- DEBUG_PRINTF( stderr,
- " long instance_size = %ld\n", theClass->instance_size);
- DEBUG_PRINTF( stderr,
- " IvarList_t ivars = %#x\n", theClass->ivars);
- DEBUG_PRINTF( stderr,
- " MethodList_t methods = %#x\n", theClass->methods);
- DEBUG_PRINTF( stderr,
- " Cache_t cache = %#x\n", theClass->cache);
- DEBUG_PRINTF( stderr, "}[%d];\n", i++);
- }
- }
-
-